@model WalkingTec.Mvvm.Core.BaseVM
<style>
    a {
        color: #01aaed
    }
</style>
<p>BasePagedListVM是框架中负责分页展示数据和导出的VM，它继承自BaseVM</p>
<wt:fieldset field-set-style="Simple" title="建立一个BasePagedListVM">
</wt:fieldset>
    <p>建立BasePagedListVM主要有以下几部</p>
    <p>1. 定义继承自BasePagedListVM的类，将关联的Model作为泛型变量</p>
    <p>2. 重写GetSearchQuery函数，将查询语句和逻辑写在这个函数里</p>
    <p>3. 重写InitGridHeader函数，这个函数定义列表显示的列</p>
    <p>4. 重写InitGridAction函数，这个函数定义列表中需要出现的操作按钮</p>
    <p>下面以学校模型为例，展示如何编写一个简单的ListVM，学校模型的定义可以参考<a href="/#/QuickStart/FirstModule">第一个模块</a></p>
    <wt:code title="SchoolListVM.cs">
public class SchoolListVM : BasePagedListVM&ltSchool, BaseSearcher&gt
{
    protected override List&ltGridAction&gt InitGridAction()
    {
        return new List&ltGridAction&gt
        {
            this.MakeStandardAction("School", GridActionStandardTypesEnum.Create, "新建","", dialogWidth: 800),
            this.MakeStandardAction("School", GridActionStandardTypesEnum.Edit, "修改","", dialogWidth: 800),
            this.MakeStandardAction("School", GridActionStandardTypesEnum.Delete, "删除", "",dialogWidth: 800),
            this.MakeStandardAction("School", GridActionStandardTypesEnum.Details, "详细","", dialogWidth: 800),
            this.MakeStandardAction("School", GridActionStandardTypesEnum.BatchEdit, "批量修改","", dialogWidth: 800),
            this.MakeStandardAction("School", GridActionStandardTypesEnum.BatchDelete, "批量删除","", dialogWidth: 800),
            this.MakeStandardAction("School", GridActionStandardTypesEnum.Import, "导入","", dialogWidth: 800),
            this.MakeStandardExportAction(null,false,ExportEnum.Excel)
        };
    }

    protected override IEnumerable&ltIGridColumn&ltSchool&gt&gt InitGridHeader()
    {
        return new List&ltGridColumn&ltSchool&gt&gt{
            this.MakeGridHeader(x =&gt x.SchoolCode),
            this.MakeGridHeader(x =&gt x.SchoolName),
            this.MakeGridHeader(x =&gt x.SchoolType),
            this.MakeGridHeaderAction(width: 200)
        };
    }

    public override IOrderedQueryable&ltSchool&gt GetSearchQuery()
    {
        var query = DC.Set&ltSchool&gt().OrderBy(x =&gt x.ID);
        return query;
    }
}
    </wt:code>
    <p>上面代码建立了一个简单的ListVM，它没有任何搜索条件，默认每页20行查询所有School的数据，并使用输出列表同样的逻辑来完成导出Excel。同时它定义了新建，修改等按钮</p>
    <wt:quote>
        <p>RecordsPerPage属性可以设置每页行数，默认是20行，NeedPage属性可以设定是否要进行分页</p>
        <p>MakeStandardExportAction的作用是在列表上生成一个导出按钮</p>
        <p>MakeGridHeaderAction的作用是在列表上增加一个操作列，操作列中出现的按钮是在InitGridAction中定义的所有ShowInRow为true的动作。当使用MakeStandardAction创建默认动作时，修改，删除和详细都是每行显示的</p>
    </wt:quote>
    <wt:quote>
        <p>MakeStandardAction用来生成标准的新建，修改等按钮，如果是自定义的按钮请使用MakeAction，MakeAction返回一个GridAction，GridAction的主要属性有：</p>
        <p>关于按钮设置的详细信息请参见<a href="/#/VM/ListAction">列表按钮</a>文档</p>
    </wt:quote>
    <wt:quote>
        <p>MakeGridHeader用来指定列表的某一列，我们可以通过一些流函数来设置一些属性来改变单元格的前景色，背景色，列头文字，列宽等等。主要流函数有：</p>
    </wt:quote>
    <table lay-filter="parse-table-demo">
        <thead>
            <tr>
                <th lay-data="{field:'username', width:200}">函数</th>
                <th lay-data="{field:'joinTime', width:400}">描述</th>
            </tr>
        </thead>
        <tbody>
            <tr>
                <td>SetBackGroundFunc</td>
                <td>设置背景色,返回类似FF0000这种格式的字符串代表颜色</td>
            </tr>
            <tr>
                <td>SetForeGroundFunc</td>
                <td>设置前景色,返回类似FF0000这种格式的字符串代表颜色</td>
            </tr>
            <tr>
                <td>SetChildren</td>
                <td>设置子表头</td>
            </tr>
            <tr>
                <td>SetFormat</td>
                <td>设置自定义格式</td>
            </tr>
            <tr>
                <td>SetFlex</td>
                <td>设置是否列宽需要充满空白区域</td>
            </tr>
            <tr>
                <td>SetWidth</td>
                <td>
                    设置列宽
                </td>
            </tr>
            <tr>
                <td>SetSort</td>
                <td>
                    设置是否需要动态排序
                </td>
            </tr>
            <tr>
                <td>SetShowTotal</td>
                <td>
                    设置是否显示汇总
                </td>
            </tr>
        </tbody>
    </table>
    <wt:quote>
      <p>关于列设置的详细信息请参见<a href="/#/VM/ListColumn">列表列文档</a> </p>
    </wt:quote>

<wt:fieldset field-set-style="Simple" title="使用BasePagedListVM">
    <p>在Controller中使用BasePagedListVM的步骤也是一目了然，请看下面的例子</p>
    <wt:code title="SchoolController.cs">
#region 搜索
[ActionDescription("搜索")]
public ActionResult Index()
{
    var vm = CreateVM&ltSchoolListVM&gt();
    return PartialView(vm);
}
#endregion
    </wt:code>

    <wt:quote>
        <p>必须使用CreateVM函数来创建ViewModel，而不要直接new。CreateVM函数会将当前Controller的Session，ModelState等信息传递给VM，并进行一些框架内部的操作</p>
    </wt:quote>
</wt:fieldset>

<wt:fieldset field-set-style="Simple" title="Searcher">
    <p>上面的例子并没有任何搜索条件，真实场景中这种情况是很少的，要在列表中加入搜索条件，有两个步骤</p>
    <p>1. 定义一个继承自BaseSearcher的类，将搜索条件作为类的属性，并把ListVM类的第二个泛型变量设定为这个Searcher</p>
    <p>2. 在GetSearchQuery函数中使用Searcher的数据来编写查询语句</p>
    <p>比如建立一个下面的Searcher用于搜索学校数据</p>
    <wt:code title="SchoolSearcher.cs">
public class SchoolSearcher : BaseSearcher
{
    [Display(Name = "学校编码")]
    public String SchoolCode { get; set; }
    [Display(Name = "学校名称")]
    public String SchoolName { get; set; }
    [Display(Name = "学校类型")]
    public SchoolTypeEnum? SchoolType { get; set; }
}
    </wt:code>
    <p>然后修改SchoolListVM，将它的定义和SchoolSearcher关联 public class SchoolListVM : BasePagedListVM&ltSchool, SchoolSearcher&gt</p>
    <p>最后修改GetSearchQuery，使用Searcher的数据</p>
    <wt:code title="SchoolVM.cs">
public override IOrderedQueryable&ltSchool&gt GetSearchQuery()
{
    var query = DC.Set&ltSchool&gt()
        .CheckContain(Searcher.SchoolCode, x=&gtx.SchoolCode)
        .CheckContain(Searcher.SchoolName, x=&gtx.SchoolName)
        .CheckEqual(Searcher.SchoolType, x=&gtx.SchoolType)
        .OrderBy(x =&gt x.ID);
    return query;
}
    </wt:code>
    <wt:quote>
        <p>CheckContain，CheckEqual等是框架提供的便捷函数，用来替换where语句，并优化生成的查询语句</p>
    </wt:quote>

</wt:fieldset>


<wt:fieldset field-set-style="Simple" title="表格中使用控件">
    <p>在项目中，我们还经常遇到使用grid来批量编辑数据的情况，</p>
    <p>请看下面的例子</p>
    <wt:code title="SchoolListVM2.cs">
    public class StudentListVm : BasePagedListVM&ltStudent, StudentSearcher&gt
    {
        public StudentListVm()
        {
            DetailGridPrix = "Students";
        }

        protected override List&ltGridAction&gt InitGridAction()
        {
            return new List&ltGridAction&gt
            {
                this.MakeStandardAction( "Employee", GridActionStandardTypesEnum.AddRow, ""),
                this.MakeStandardAction( "Employee", GridActionStandardTypesEnum.RemoveRow, ""),

            };
        }

        protected override IEnumerable&ltIGridColumn&ltStudent&gt&gt InitGridHeader()
        {
            return new List&ltGridColumn&ltStudent&gt&gt{
                this.MakeGridHeader( x =&gt x.LoginName).SetEditType( EditTypeEnum.TextBox),
                this.MakeGridHeader( x =&gt x.Name).SetEditType( EditTypeEnum.TextBox),
                this.MakeGridHeader( x =&gt x.Sex)
      .SetEditType( EditTypeEnum.ComboBox, typeof( Models.SexEnum).ToListItems( pleaseSelect:true)),
                this.MakeGridHeader( x =&gt x.CellPhone).SetEditType( EditTypeEnum.TextBox),
                this.MakeGridHeader( x=&gtx.IsValid).SetEditType( EditTypeEnum.CheckBox)
            };
        }

        public override IOrderedQueryable&ltStudent&gt GetSearchQuery()
        {
            List&ltStudent&gt data = new List&ltStudent&gt
            {
                new Student{ LoginName = "zhangsan", Name="张三", Sex= Models.SexEnum.Male, CellPhone="13012213483", ExcelIndex = 0, IsValid = true, ID = new Guid("6F5C2D15-4871-4083-B269-06F456A4F1B6")},
                new Student{ LoginName = "lisi", Name="李四", Sex= Models.SexEnum.Male, CellPhone="13075829654", ExcelIndex = 1, IsValid = false, ID = new Guid("9C7BC358-B8BD-4547-AFC1-11BF6F2B608B")},
                new Student{ LoginName = "wangwu", Name="王五", Sex= Models.SexEnum.Male, CellPhone="13098635100", ExcelIndex = 2, IsValid = true, ID = new Guid("3BF9217C-1ACF-4D80-9899-42CFDA4C8746")},
                new Student{ LoginName = "zhaoliu", Name="赵六", Sex= Models.SexEnum.Female, CellPhone="13035698123", ExcelIndex = 3, IsValid = false, ID = new Guid("0C7F6A24-A08D-46BD-86AC-6B6A391A9F04")},
            };

            var query = data.AsQueryable().Where(x=&gt
                    (string.IsNullOrEmpty( Searcher.LoginName) || x.LoginName.Contains( Searcher.LoginName)) &&
                    (string.IsNullOrEmpty( Searcher.Name) || x.Name.Contains( Searcher.Name)) &&
                    (Searcher.Sex == null || x.Sex == Searcher.Sex)
                )
                .OrderBy(x =&gt x.ExcelIndex);
            return query;
        }

    }


    </wt:code>
    <p>上面代码中，我们首先指定了DetailGridPrix字段，这个字段是告诉框架提交之后应该将数据保存在哪里，比如我们设定DetailGridPrix="Students"，那么提交的时候，框架会使用Students[0].Id,Students[0].Name,Students[1].Id,Students[1].Name这种形式来提交数据</p>
    <p>然后在InitGridAction方法中，我们定义了两个按钮，分别是AddRow和RemoveRow类型。这两个按钮负责动态给Grid添加或者删除行</p>
    <p>最后再InitGridHeader方法中，我们使用SetEditType方法给每一列定义需要使用的控件</p>
</wt:fieldset>

<wt:fieldset field-set-style="Simple" title="使用存储过程">
  </wt:fieldset>

    <p>有时我们需要使用sql语句或者存储过程来查询数据</p>
    <p>请看下面的例子</p>
    <wt:code title="SchoolListVM2.cs">
    public class ActionLogListVM : BasePagedListVM&lt;ActionLog,BaseSearcher&gt;
    {
        protected override List&lt;GridAction&gt; InitGridAction()
        {
            var actions = new List&lt;GridAction&gt;
            {
                this.MakeStandardAction("ActionLog", GridActionStandardTypesEnum.Details, "详细","_Admin", dialogWidth: 800).SetHideOnToolBar(true),
                this.MakeStandardExportAction(null,false,ExportEnum.Excel)
            };
            return actions;
        }

        protected override IEnumerable&lt;IGridColumn&lt;ActionLog&gt;&gt; InitGridHeader()
        {
            var header = new List&lt;GridColumn&lt;ActionLog&gt;&gt;();

            header.Add(this.MakeGridHeader(x =&gt; x.LogType, 80));
            header.Add(this.MakeGridHeader(x =&gt; x.ModuleName, 120));
            header.Add(this.MakeGridHeader(x =&gt; x.ActionName, 120));
            header.Add(this.MakeGridHeader(x =&gt; x.ITCode, 120));
            header.Add(this.MakeGridHeader(x =&gt; x.ActionUrl, 200));
            header.Add(this.MakeGridHeader(x =&gt; x.ActionTime, 200).SetSort(true));
            header.Add(this.MakeGridHeader(x =&gt; x.Duration, 100).SetSort(true));
            header.Add(this.MakeGridHeader(x =&gt; x.IP, 120));
            header.Add(this.MakeGridHeader(x =&gt; x.Remark));
            header.Add(this.MakeGridHeaderAction(width: 120));

            return header;
        }

        public override DbCommand GetSearchCommand()
        {
            var sql = string.Format("SELECT top 10 * from actionlogs");
            var cmd = DC.Database.GetDbConnection().CreateCommand();
            cmd.CommandText = sql;
            cmd.CommandType = CommandType.Text;
            return cmd;
        }
    }


    </wt:code>
    <p>上面代码中，我们重写了GetSearchCommand方法，它返回一个DbCommand，我们可以指定sql语句或者存储过程</p>
    <wt:quote>
        <p>如果使用存储过程，框架自带的分页，排序等功能只能依靠你在存储过程里自己完成</p>
        <p>框架会自动把下列信息作为变量添加到存储过程里</p>
    <table lay-filter="parse-table-demo">
        <thead>
            <tr>
                <th lay-data="{field:'username', width:200}">变量</th>
                <th lay-data="{field:'joinTime', width:400}">描述</th>
            </tr>
        </thead>
        <tbody>
            <tr>
                <td>@@SearchMode</td>
                <td>搜索类别</td>
            </tr>
            <tr>
                <td>@@NeedPage</td>
                <td>是否需要分页</td>
            </tr>
            <tr>
                <td>@@CurrentPage</td>
                <td>当前页</td>
            </tr>
            <tr>
                <td>@@RecordsPerPage</td>
                <td>每页条数</td>
            </tr>
            <tr>
                <td>@@Sort</td>
                <td>排序字段</td>
            </tr>
            <tr>
                <td>@@SortDir</td>
                <td>升序或降序</td>
            </tr>
            <tr>
                <td>@@IDs</td>
                <td>当SearchMode为CheckExport，勾选导出时，IDs会传用户勾选的ID</td>
            </tr>
            <tr>
                <td>@@TotalRecords</td>
                <td>output变量，返回所有记录数</td>
            </tr>
        </tbody>
    </table>
        <p>使用存储过程比较繁琐，绝大部分需求时可以使用lambda或者linq来写的，不到万不得已尽量别用存储过程</p>
    </wt:quote>

    <wt:code title="SchoolListVM2.cs">
    public class ActionLogListVM : BasePagedListVM&lt;ActionLog,BaseSearcher&gt;
    {
        protected override List&lt;GridAction&gt; InitGridAction()
        {
            var actions = new List&lt;GridAction&gt;
            {
                this.MakeStandardAction("ActionLog", GridActionStandardTypesEnum.Details, "详细","_Admin", dialogWidth: 800).SetHideOnToolBar(true),
                this.MakeStandardExportAction(null,false,ExportEnum.Excel)
            };
            return actions;
        }

        protected override IEnumerable&lt;IGridColumn&lt;ActionLog&gt;&gt; InitGridHeader()
        {
            var header = new List&lt;GridColumn&lt;ActionLog&gt;&gt;();

            header.Add(this.MakeGridHeader(x =&gt; x.LogType, 80));
            header.Add(this.MakeGridHeader(x =&gt; x.ModuleName, 120));
            header.Add(this.MakeGridHeader(x =&gt; x.ActionName, 120));
            header.Add(this.MakeGridHeader(x =&gt; x.ITCode, 120));
            header.Add(this.MakeGridHeader(x =&gt; x.ActionUrl, 200));
            header.Add(this.MakeGridHeader(x =&gt; x.ActionTime, 200).SetSort(true));
            header.Add(this.MakeGridHeader(x =&gt; x.Duration, 100).SetSort(true));
            header.Add(this.MakeGridHeader(x =&gt; x.IP, 120));
            header.Add(this.MakeGridHeader(x =&gt; x.Remark));
            header.Add(this.MakeGridHeaderAction(width: 120));

            return header;
        }

        public override DbCommand GetSearchCommand()
        {
            cmd.CommandText = "getactionlog";
            cmd.CommandType = CommandType.StoredProcedure;
            return cmd;
        }
    }
    </wt:code>
    <p>上面代码中，ListVM会调用名为getactionlog的存储过程，存储过程示例如下</p>
<wt:code>
  CREATE PROCEDURE getactionlog
	@@SearchMode varchar,
	@@NeedPage bit,
	@@CurrentPage int,
	@@RecordsPerPage int,
	@@Sort varchar = '',
	@@SortDir varchar = '',
	@@IDs varchar = '',
	@@TotalRecords int output
AS
	select @@TotalRecords =  count(*) from ActionLogs;
	if @@NeedPage = 1 begin
	select * from ActionLogs order by CreateTime desc
	offset ((@@CurrentPage-1)*@@RecordsPerPage) rows
	fetch next @@RecordsPerPage rows only;
	end
	else begin
	select * from ActionLogs order by CreateTime desc
	end 
RETURN 0
</wt:code>
    <p>存储过程的前8个参数都是框架自动按顺序添加的，自己的搜索条件可以在后面继续加</p>

<wt:fieldset field-set-style="Simple" title="ListVM主要属性">
</wt:fieldset>
    <table lay-filter="parse-table-demo">
        <thead>
            <tr>
                <th lay-data="{field:'username', width:200}">属性</th>
                <th lay-data="{field:'joinTime', width:400}">描述</th>
            </tr>
        </thead>
        <tbody>
            <tr>
                <td>Searcher</td>
                <td>获取关联的搜索类</td>
            </tr>
            <tr>
                <td>EntityList</td>
                <td>获取搜索结果</td>
            </tr>
            <tr>
                <td>RecordsPerPage</td>
                <td>分页行数，默认时20条</td>
            </tr>
            <tr>
                <td>NeedPage</td>
                <td>是否需要分页，默认为true</td>
            </tr>
            <tr>
                <td>SearcherMode</td>
                <td>搜索模式，用来判断当前搜索是普通列表，导出，批量操作等</td>
            </tr>

        </tbody>
    </table>
<wt:fieldset field-set-style="Simple" title="ListVM主要函数">
 </wt:fieldset>
   <table lay-filter="parse-table-demo">
        <thead>
            <tr>
                <th lay-data="{field:'username', width:200}">函数</th>
                <th lay-data="{field:'joinTime', width:400}">描述</th>
            </tr>
        </thead>
        <tbody>
            <tr>
                <td>InitGridAction()</td>
                <td>初始化列表需要显示的操作按钮</td>
            </tr>
            <tr>
                <td>MakeStandardAction()</td>
                <td>根据默认设置生成添加，修改，删除，详细，导入的按钮</td>
            </tr>
            <tr>
                <td>MakeStandardExportAction()</td>
                <td>生成导出Excel的按钮</td>
            </tr>
            <tr>
                <td>InitGridHeader()</td>
                <td>初始化列表需要显示的列</td>
            </tr>
            <tr>
                <td>GetSearchQuery()</td>
                <td>获取查询语句</td>
            </tr>
            <tr>
                <td>GetExportQuery()</td>
                <td>获取导出时的查询语句，如不重写，默认调用GetSearchQuery</td>
            </tr>
            <tr>
                <td>GetCheckedExportQuery()</td>
                <td>获取勾选指定数据导出时的查询语句，如不重写，默认调用GetSearchQuery</td>
            </tr>
            <tr>
                <td>GetSelectorQuery()</td>
                <td>获取选择器模式下的查询语句，如不重写，默认调用GetSearchQuery</td>
            </tr>
            <tr>
                <td>GetBatchQuery</td>
                <td>获取批量模式下的查询语句，如不重写，默认调用GetSearchQuery</td>
            </tr>
        </tbody>
    </table>
    <script>
    layui.use('code',function(){layui.code({ about: false })})
    </script>
    <script>
        layui.table.init('parse-table-demo', {
        limit: 100, page: false
        });
    </script>
<script>
  $("#@Model.ViewDivId").parent().css("height", "auto");
</script>
